import 'dart:convert';
import 'package:freedom/util/data_type.dart';

class UserState {
  String? username;
  String? portraitUrl;
  bool banker;
  int seq;
  int userId;
  bool win;
  int? winCard;
  String? handCards;
  int handCardCount;
  int? touchCard;
  String? exportCards;
  String? waitCompleted;
  String? beforeCompleted;
  int score;
  int winNumber;
  bool onHook = false;
  int? exportCard;
  int? timestamp;
  String? listenCards;
  List<int>? listenCardArray;
  List<OpenDeal>? openDeal;
  bool online = true;
  bool listen = false;
  bool touchCardAction = false;

  bool startGaming = false;

  String? swapCardOut;
  String? swapCardIn;
  int? swapUserId;

  bool swapOutStatus = false;
  bool swapInStatus = false;

  List<Fraction>? fractions;

  //选中
  int? selectCard;

  /// 判断是否已经是听牌
  bool checkListenCard() {
    if (listenCardArray == null || listenCardArray!.isEmpty) {
      return false;
    }
    return (listenCardArray?.length ?? 0) % 3 == 2;
  }

  void setListenCard(List<int> allCards) {
    listenCards = allCards.join(",");
    listenCardArray = List.from(allCards);
  }

  /// 是否存在某个事件
  bool checkWaitActionExits(String action) {
    if (waitCompleted == null) {
      return false;
    }
    return getActions().contains(action);
  }

  bool checkBeforeAction(String action) {
    if (beforeCompleted == null) {
      return false;
    }
    return beforeCompleted!.contains(action);
  }

  UserState copyWith(UserState userState) {
    return UserState(
        selectCard: userState.selectCard,
        username: userState.username,
        waitCompleted: userState.waitCompleted,
        openDeal: userState.openDeal,
        userId: userState.userId,
        seq: userState.seq,
        portraitUrl: userState.portraitUrl,
        exportCard: userState.exportCard,
        exportCards: userState.exportCards,
        handCardCount: userState.handCardCount,
        handCards: userState.handCards,
        score: userState.score,
        touchCard: userState.touchCard,
        beforeCompleted: userState.beforeCompleted,
        swapCardOut: userState.swapCardOut,
        swapCardIn: userState.swapCardIn,
        swapUserId: userState.swapUserId,
        swapOutStatus: userState.swapOutStatus,
        swapInStatus: userState.swapInStatus,
        banker: userState.banker,
        fractions: userState.fractions,
        listen: userState.listen,
        online: userState.online,
        winNumber: userState.winNumber,
        listenCards: userState.listenCards,
        startGaming: userState.startGaming,
        onHook: userState.onHook,
        listenCardArray: userState.listenCardArray,
        timestamp: DateTime.now().millisecondsSinceEpoch);
  }

  // 获取所有能杠的牌
  List<int> allBarCard() {
    List<int> userCards = getUserHandCards();
    List<int> result = [];
    if (userCards.length % 3 != 2) {
      return result;
    }
    Map<int, int> countMap = {};
    for (int num in userCards) {
      if (countMap.containsKey(num)) {
        countMap[num] = countMap[num]! + 1;
      } else {
        countMap[num] = 1;
      }
      if (countMap[num]! >= 4) {
        result.add(num);
      }
      if (openDeal?.any((element) => element.card == num) ?? false) {
        result.add(num);
      }
    }
    return result;
  }

  // 摸牌 有时候摸牌 只是因为碰导致变成了需要出牌
  void addTouchCard(int? cardCode) {
    handCardCount += 1;
    if (cardCode == null) {
      return;
    }
    touchCard = cardCode;
    List<int> cards =
        handCards?.split(",").toList().map((e) => int.parse(e)).toList() ?? [];
    if (cards.length % 3 == 2) {
      cards.remove(cardCode);
      handCards = cards.join(",");
    }
  }

  // 出牌
  void exportCardFunc(int? card) {
    handCardCount = handCardCount - 1;
    var allCards = getUserHandCards();
    if (allCards.isNotEmpty) {
      allCards.remove(card);
      handCards = allCards.join(",");
    }
    touchCard = null;
    waitCompleted = null;
  }

  factory UserState.empty() => UserState();

  UserState(
      {this.username,
      this.banker = false,
      this.portraitUrl,
      this.seq = 1,
      this.userId = 0,
      this.win = false,
      this.score = 0,
      this.handCards,
      this.exportCards,
      this.exportCard,
      this.winCard,
      this.selectCard,
      this.timestamp,
      this.beforeCompleted,
      this.handCardCount = 0,
      this.touchCard,
      this.waitCompleted,
      this.winNumber = 0,
      this.swapCardOut,
      this.swapCardIn,
      this.swapUserId,
      this.online = true,
      this.fractions,
      this.listen = false,
      this.startGaming = false,
      this.onHook = false,
      this.listenCards,
      this.listenCardArray,
      this.swapOutStatus = false,
      this.swapInStatus = false,
      this.openDeal});

  UserState exportCardAction(int card) {
    return UserState(
        exportCard: card,
        username: username,
        portraitUrl: portraitUrl,
        score: score,
        timestamp: DateTime.now().millisecondsSinceEpoch);
  }

  // 用timestamp 时间戳保证事件随时可以更新
  UserState selectCardAction(int? card) {
    return copyWith(this)..selectCard = card;
  }

  factory UserState.fromJson(Map<String, dynamic>? jsonRes) {
    if (jsonRes == null) {
      return UserState();
    }

    final List<OpenDeal> openDeal =
        jsonRes['openDeal'] is List ? <OpenDeal>[] : [];

    for (final dynamic item in jsonRes['openDeal'] ?? []) {
      if (item != null) {
        openDeal.add(OpenDeal.fromJson(item));
      }
    }

    openDeal.sort((a, b) => a.card.compareTo(b.card));

    final List<Fraction> fractions =
        jsonRes['fractions'] is List ? <Fraction>[] : [];

    for (final dynamic item in jsonRes['fractions'] ?? []) {
      if (item != null) {
        fractions.add(Fraction.fromJson(item));
      }
    }
    var listenCards = asT<String>(jsonRes["listenCards"]);
    return UserState(
        username: asT<String>(jsonRes["username"]),
        portraitUrl: asT<String>(jsonRes["portraitUrl"]),
        handCards: asT<String>(jsonRes["handCards"]),
        exportCards: asT<String>(jsonRes["exportCards"]),
        handCardCount: asT<int>(jsonRes["handCardCount"])!,
        seq: asT<int>(jsonRes["seq"]) ?? 1,
        userId: asT<int>(jsonRes["userId"]) ?? 0,
        score: asT<int>(jsonRes["score"])!,
        touchCard: asT<int>(jsonRes["touchCard"]),
        selectCard: asT<int>(jsonRes["selectCard"]),
        waitCompleted: asT<String>(jsonRes["waitCompleted"]),
        beforeCompleted: asT<String>(jsonRes["beforeCompleted"]),
        banker: asT<bool>(jsonRes["banker"]) ?? false,
        win: asT<bool>(jsonRes["win"]) ?? false,
        online: asT<bool>(jsonRes["online"]) ?? true,
        listen: asT<bool>(jsonRes["listen"]) ?? false,
        onHook: asT<bool>(jsonRes["onHook"]) ?? false,
        winCard: asT<int>(jsonRes["winCard"]) ?? 0,
        swapCardOut: asT<String>(jsonRes["swapCardOut"]),
        swapCardIn: asT<String>(jsonRes["swapCardIn"]),
        swapUserId: asT<int>(jsonRes["swapUserId"]),
        winNumber: asT<int>(jsonRes["winNumber"]) ?? 0,
        swapOutStatus: asT<bool>(jsonRes["swapOutStatus"]) ?? false,
        swapInStatus: asT<bool>(jsonRes["swapInStatus"]) ?? false,
        listenCards: listenCards,
        startGaming: false,
        listenCardArray: listenCards?.split(',').map(int.parse).toList(),
        fractions: fractions,
        openDeal: openDeal);
  }

  Map<String, dynamic> toJson() => <String, dynamic>{
        "username": username,
        "portraitUrl": portraitUrl,
        "handCardCount": handCardCount,
        "exportCards": exportCards,
        "handCards": handCards,
        "userId": userId,
        "seq": seq,
        "waitCompleted": waitCompleted,
        "touchCard": touchCard,
        "online": online,
        "listen": listen,
        "score": score,
        "banker": banker,
        "selectCard": selectCard
      };

  @override
  String toString() {
    return json.encode(this);
  }

  /// 获取用户的手牌  + 摸牌
  List<int> getUserHandCards({bool addTouch = true}) {
    if (handCards == null || handCards == "") {
      return [];
    }
    List<int> cards =
        handCards!.split(",").toList().map((e) => int.parse(e)).toList();
    //最后一张摸牌放在最后
    if (touchCard != null && addTouch) {
      cards.add(touchCard!);
    }
    //升序排列
    cards.sort();
    return cards;
  }

  /// 按照数组移出指定的牌
  ///
  /// 在换牌中使用
  /// 首次不需要设置touchCard 所以这里直接设置手牌

  void removeAssignCard(List<int>? listCard) {
    if (listCard == null) {
      return;
    }
    handCardCount = handCardCount - listCard.length;
    List<int> userHandCard = getUserHandCards();
    if (listCard.isEmpty) {
      return;
    }
    userHandCard.sort();
    for (int number in listCard) {
      for (int i = 0, j = userHandCard.length - 1; j >= i; j--) {
        if (userHandCard[j] == number) {
          userHandCard.removeAt(j);
          break;
        }
      }
    }
    if (userHandCard.length % 3 == 2) {
      touchCard = userHandCard.removeLast();
      handCards = userHandCard.join(",");
    } else {
      touchCard = null;
      handCards = userHandCard.join(",");
    }
  }

  void removeSwapCard(List<int>? listCard) {
    handCardCount -= 3;
    if (listCard == null) {
      return;
    }
    removeAssignCard(listCard);
  }

  /// 交换期间 摸牌字段应该是空字段
  void addSwapCard(List<int>? listCard) {
    handCardCount += 3;
    if (listCard == null) {
      return;
    }
    List<int> userHandCard = getUserHandCards();
    if (userHandCard.isEmpty) {
      return;
    }
    if (listCard.length != 3) {
      return;
    }
    for (var element in listCard) {
      userHandCard.add(element);
    }
    userHandCard.sort();
    if (userHandCard.length % 3 == 2) {
      touchCard = userHandCard.removeLast();
      handCards = userHandCard.join(",");
    } else {
      touchCard = null;
      handCards = userHandCard.join(",");
    }
  }

  /// @type  1 碰牌,2 暗杠, 3 明杠 , 4 点杠
  void addOpenDeal(int card, int type, {int? sourceTarget}) {
    openDeal ??= [];
    if (type == 1) {
      // 移出两张手牌
      removeAssignCard([card, card]);
      openDeal!.add(OpenDeal.fromObj(
          card: card, type: type, targetUserSeq: sourceTarget));
    } else if (type == 2) {
      // 暗杠 需要移出4张  并且添加
      removeAssignCard([card, card, card, card]);
      openDeal!.add(OpenDeal.fromObj(
          card: card, type: type, targetUserSeq: sourceTarget));
    } else if (type == 3) {
      // 明杠 需要移出1张  并且添加
      removeAssignCard([card]);
      openDeal!.removeWhere((element) => element.card == card);
      openDeal!.add(OpenDeal.fromObj(
          card: card, type: type, targetUserSeq: sourceTarget));
    } else if (type == 4) {
      // 点杠 需要移出3张  并且添加
      removeAssignCard([card, card, card]);
      openDeal!.removeWhere((element) => element.card == card);
      openDeal!.add(OpenDeal.fromObj(
          card: card, type: type, targetUserSeq: sourceTarget));
    }
    openDeal?.sort((a, b) => a.card.compareTo(b.card));
  }

  /// 根据数据库回传的字段
  /// 处理数据
  List<int> getExportCards() {
    if (exportCards == null || exportCards == "") {
      return [];
    }
    List<int> cards =
        exportCards!.split(",").toList().map((e) => int.parse(e)).toList();
    return cards;
  }

  /// 获取待处理行为
  List<String> getActions() {
    if (waitCompleted == null || waitCompleted == "") {
      return [];
    }
    return waitCompleted!.split(",").toList();
  }

  /// 获取上一次处理的行为

  List<String> getBeforeActions() {
    if (beforeCompleted == null || beforeCompleted == "") {
      return [];
    }
    return beforeCompleted!.split(",").toList();
  }

  /// 获取上一次处理的行为
  ///

  void cleanActions() {
    waitCompleted = null;
  }

  @override
  List<Object?> get props =>
      [exportCard, selectCard, exportCards, handCards, timestamp];
}

class OpenDeal {
  int? id;
  int type = 1;
  int card = 1;
  List<int>? cardList;

  int? sourceUserSeq;
  int? sourceUserId;
  int? targetUserId;
  int? targetUserSeq;

  /// @type  1 碰牌,2 暗杠, 3 明杠 , 4 点杠
  int getCardCount() {
    return type == 1 ? 3 : 4;
  }

  OpenDeal(
      {this.id,
      this.type = 1,
      this.card = 1,
      this.cardList,
      this.sourceUserId,
      this.sourceUserSeq,
      this.targetUserId,
      this.targetUserSeq});

  factory OpenDeal.fromObj(
      {int? targetUserSeq, int? userId, required int type, required int card}) {
    List<int> cardList = [];
    int count = 3;
    if (type != 1) {
      count = 4;
    }
    for (int i = 0; i < count; i++) {
      cardList.add(card);
    }
    return OpenDeal(
        targetUserSeq: targetUserSeq,
        sourceUserId: userId,
        type: type,
        card: card,
        cardList: cardList);
  }

  factory OpenDeal.fromJson(Map<String, dynamic>? jsonRes) {
    if (jsonRes == null) {
      return OpenDeal();
    }
    int type = asT<int>(jsonRes["type"]) ?? 1;
    int card = asT<int>(jsonRes["card"]) ?? 1;
    int count = 3;
    List<int> cardList = [];
    if (type != 1) {
      count = 4;
    }
    for (int i = 0; i < count; i++) {
      cardList.add(card);
    }
    return OpenDeal(
        id: asT<int>(jsonRes["id"]),
        targetUserSeq: asT<int>(jsonRes["targetUserSeq"]),
        targetUserId: asT<int>(jsonRes["targetUserId"]),
        sourceUserSeq: asT<int>(jsonRes["sourceUserSeq"]),
        sourceUserId: asT<int>(jsonRes["sourceUserId"]),
        type: type,
        card: card,
        cardList: cardList);
  }
}

class Fraction {
  int? id;
  int? sourceUserId;
  String sourceUserName;
  String targetUserName;
  int? targetUserId;
  String? fractionInfo;
  int fraction;

  Fraction(
      {this.id,
      this.sourceUserId,
      this.targetUserId,
      this.fractionInfo,
      this.sourceUserName = "",
      this.targetUserName = "",
      this.fraction = 0});

  factory Fraction.fromJson(Map<String, dynamic>? jsonRes) {
    if (jsonRes == null) {
      return Fraction();
    }
    return Fraction(
      id: asT<int>(jsonRes["id"]),
      sourceUserId: asT<int>(jsonRes["sourceUserId"]),
      targetUserId: asT<int>(jsonRes["targetUserId"]),
      fraction: asT<int>(jsonRes["fraction"]) ?? 0,
      fractionInfo: asT<String>(jsonRes["fractionInfo"]),
      sourceUserName: asT<String>(jsonRes["sourceUserName"]) ?? "",
      targetUserName: asT<String>(jsonRes["targetUserName"]) ?? "",
    );
  }
}
